package com.lyf.boot.modules.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.lyf.boot.base.system.entity.ChinaArea;
import com.lyf.boot.helper.SSLHelper;
import com.lyf.boot.helper.UserAgentHelper;
import com.lyf.boot.listener.event.ChinaAreaEvent;
import com.lyf.boot.modules.system.repository.ChinaAreaRepository;
import com.lyf.boot.modules.system.service.IChinaAreaService;
import com.xxl.job.core.util.DateUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author Luyufan
 * @date 2023/12/6 11:31
 **/
@Service
@RequiredArgsConstructor
@Slf4j
public class ChinaAreaServiceImpl implements IChinaAreaService {

    private final ChinaAreaRepository chinaAreaRepository;

    private final ApplicationEventPublisher eventPublisher;

    private final static String BASE_URL = "https://www.stats.gov.cn/sj/tjbz/tjyqhdmhcxhfdm/" + DateUtil.format(new Date(), DatePattern.NORM_YEAR_PATTERN) + "/";

    private final static List<String> AREA_TYPE = CollUtil.toList("province", "city",
            "county", "town", "village"
    );

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void crawlChinaArea() throws IOException {
        String rootUrl = BASE_URL + "index.html";
        Document document = SSLHelper.getConnection(rootUrl).userAgent(UserAgentHelper.randomUserAgent())
                .header("Referer", rootUrl)
                .timeout(100000).get();
        String classPrefix = AREA_TYPE.get(0);
        Elements tables = document.body().select("table." + classPrefix + "table");
        if(tables.isEmpty()){
            //请求过于频繁，页面会404
            log.warn("china area not found");
            //throw new ServiceException("china area not found");
            return;
        }
        Elements provinces = tables.get(0).select("tr." + classPrefix + "tr");
        List<ChinaArea> list = new ArrayList<>();
        provinces.forEach(province -> {
            Elements rows = province.select("td");
            rows.forEach(row -> {
                String areaName = row.select("a").text();
                String href = row.select("a").attr("href");
                String areaCode = href.substring(0, href.lastIndexOf("."));
                ChinaArea chinaArea = new ChinaArea();
                chinaArea.setAreaCode(areaCode);
                chinaArea.setAreaName(areaName);
                chinaArea.setParentCode("0");
                List<String> names = CollUtil.toList(areaName);
                chinaArea.setFullName(JSONUtil.toJsonStr(names));
                list.add(chinaArea);
                eventPublisher.publishEvent(new ChinaAreaEvent(areaCode, "city", BASE_URL + href, names));
            });
        });
        if(CollUtil.isNotEmpty(list)){
            chinaAreaRepository.saveOrUpdateBatch(list, list.size());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void crawlChinaArea(ChinaAreaEvent event) {
        String url = event.getUrl();
        String classPrefix = event.getClassPrefix();
        try {
            Document document = SSLHelper.getConnection(url).userAgent(UserAgentHelper.randomUserAgent()).header("Referer", url)
                    .timeout(10000).get();
            Elements area = document.body().select("table." + classPrefix + "table");
            if(area.isEmpty()){
                return;
            }
            List<ChinaArea> list = new ArrayList<>();
            String parentCode = event.getParentCode();
            int index = AREA_TYPE.indexOf(classPrefix);
            Elements trs = area.select("tr[class$=tr]");
            List<String> names = event.getNames();
            trs.forEach(tr -> {
                Elements tds = tr.select("td");
                Element codeTd = tds.get(0);
                Elements codeA = codeTd.select("a");
                Element nameTd = tds.get(index == AREA_TYPE.size() - 1 ? 2 : 1);
                Elements nameA = nameTd.select("a");
                String href = null;
                if(!codeA.isEmpty()){
                    href = codeA.get(0).attr("href");
                }
                String areaCode = codeA.isEmpty() ? codeTd.text() : codeA.get(0).text();
                String areaName = nameA.isEmpty() ? nameTd.text() : nameA.get(0).text();
                //log.debug("areaCode: {}, areaName: {}", areaCode, areaName);
                ChinaArea chinaArea = new ChinaArea();
                chinaArea.setAreaName(areaName);
                chinaArea.setAreaCode(areaCode);
                chinaArea.setParentCode(parentCode);
                List<String> newNames = BeanUtil.copyToList(names, String.class);
                newNames.add(areaName);
                chinaArea.setFullName(JSONUtil.toJsonStr(newNames));
                list.add(chinaArea);
                if(index < AREA_TYPE.size() && StrUtil.isNotBlank(href)){
                    String subUrl = url.substring(0, url.lastIndexOf("/") + 1) + href;
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    eventPublisher.publishEvent(new ChinaAreaEvent(areaCode, AREA_TYPE.get(index + 1), subUrl, newNames));
                }
            });
            if(CollUtil.isNotEmpty(list)){
                chinaAreaRepository.saveOrUpdateBatch(list, list.size());
            }else{
                log.debug("url: {}, parentCode: {} ", url, parentCode);
            }
        } catch (Exception e){
            log.error("href: {}, classPrefix:{}", url, classPrefix, e);
        }
        log.debug("执行完了：{}", JSONUtil.toJsonStr(event));
    }

    @Override
    public ChinaArea getById(String id) {
        return chinaAreaRepository.getById(id);
    }

}
